﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright company="Microsoft Corp." file="TestAzureManagementWebResponseFactory.cs">
//   All rights reserved. See License.txt in the project root for license information.
// </copyright>
// 
// --------------------------------------------------------------------------------------------------------------------
namespace AzureManagementTests.TestSupport
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;

    using AzureManagement.AzureDataContracts;

    using Common;

    public class TestAzureManagementWebResponseFactory : ITestWebResponseFactory
    {
        public string BaseUri { get; set; }

        public string ApiVersion { get; set; }

        public string SubscriptionId { get; set; }

        public IList<string> ManagementCertificates { get; set; }

        public IList<HostedServiceTestMock> HostedServices { get; set; }

        public IList<Operation> Operations { get; set; }

        public IList<StorageService> StorageAccounts { get; set; }

        public WebResponse CreateResponse(TestWebRequest request)
        {
            if (string.IsNullOrEmpty(request.CertificateThumbprint) || this.ManagementCertificates == null
                || !this.ManagementCertificates.Any(x => x.Equals(request.CertificateThumbprint)))
            {
                return new TestWebResponse("Invalid request.");
            }

            string body = request.GetBody();

            var versionHeader = request.Headers.Get("x-ms-version");
            if (string.IsNullOrEmpty(versionHeader) || !versionHeader.Equals(this.ApiVersion))
            {
                return new TestWebResponse("Api version is not specified.");
            }

            if (
                !this.BaseUri.Equals(
                    string.Format("{0}://{1}/", request.RequestUri.Scheme, request.RequestUri.DnsSafeHost)))
            {
                return new TestWebResponse("Invalid base Uri.");
            }

            var segments =
                request.RequestUri.AbsolutePath.Split(new[] { "/" }, StringSplitOptions.RemoveEmptyEntries).ToList();
            if (!segments.Any())
            {
                return new TestWebResponse("Invalid request uri.");
            }

            if ((request.Method.Equals("POST") || request.Method.Equals("PUT"))
                && !request.ContentType.Equals("application/xml"))
            {
                return new TestWebResponse("Invalid content type.");
            }

            if (!segments.First().Equals(this.SubscriptionId))
            {
                return new TestWebResponse("Invalid subscription id");
            }

            segments.RemoveAt(0);
            if (!segments.Any())
            {
                return new TestWebResponse("Invalid request uri.");
            }

            if (segments.First().Equals("services"))
            {
                return this.ProcessServicesRequest(request.Method, segments, body, request.RequestUri.Query);
            }

            if (segments.First().Equals("operations"))
            {
                return this.ProcessOperationTrackingRequest(request.Method, segments, body, request.RequestUri.Query);
            }

            return new TestWebResponse("Invalid request.");
        }

        private TestWebResponse ProcessOperationTrackingRequest(
            string method, List<string> segments, string body, string query)
        {
            segments.RemoveAt(0);
            if (segments.Count == 1 && method.Equals("GET"))
            {
                var operation = this.Operations.FirstOrDefault(x => x.ID.Equals(segments.First()));
                if (operation != null)
                {
                    return new TestWebResponse(XmlHelper.Serialize(operation));
                }
            }

            return new TestWebResponse("Invalid request.");
        }

        private TestWebResponse ProcessServicesRequest(string method, List<string> segments, string body, string query)
        {
            segments.RemoveAt(0);
            if (segments.Any() && segments.First().Equals("hostedservices"))
            {
                segments.RemoveAt(0);
                return this.ProcessHostedServicesRequest(method, segments, body, query);
            }

            if (segments.Any() && segments.First().Equals("storageservices"))
            {
                segments.RemoveAt(0);
                return this.ProcessStorageServicesRequest(method, segments, body, query);
            }

            return new TestWebResponse("Invalid request.");
        }

        private TestWebResponse ProcessStorageServicesRequest(
            string method, List<string> segments, string body, string query)
        {
            if (!segments.Any())
            {
                if (method.Equals("GET"))
                {
                    return this.ListStorageServices();
                }

                if (method.Equals("POST"))
                {
                    var createStorageService = XmlHelper.Deserialize<CreateStorageServiceInput>(body);
                    if (this.ValidateStorageService(createStorageService))
                    {
                        this.StorageAccounts.Add(
                            new StorageService
                                {
                                    ServiceName = createStorageService.ServiceName, 
                                    Url = string.Empty, 
                                    StorageServiceProperties =
                                        new StorageServiceProperties
                                            {
                                                Description =
                                                    createStorageService
                                                    .Description, 
                                                Label = createStorageService.Label, 
                                                AffinityGroup =
                                                    createStorageService
                                                    .AffinityGroup, 
                                                Location =
                                                    createStorageService.Location, 
                                                GeoReplicationEnabled =
                                                    createStorageService
                                                    .GeoReplicationEnabled
                                            }, 
                                    StorageServiceKeys =
                                        new StorageServiceKeys
                                            {
                                                Primary = Guid.NewGuid().ToString(), 
                                                Secondary = Guid.NewGuid().ToString()
                                            }
                                });

                        return new TestWebResponse("Storage service has been created.");
                    }
                }

                return new TestWebResponse("Invalid request.");
            }

            var serviceName = segments.First();
            var storageService = this.StorageAccounts.FirstOrDefault(x => x.ServiceName.Equals(serviceName));
            if (storageService == null)
            {
                return new TestWebResponse("Invalid storage service name.");
            }

            segments.RemoveAt(0);
            if (segments.Any())
            {
                if (segments.First().Equals("keys"))
                {
                    segments.RemoveAt(0);
                    if (!segments.Any())
                    {
                        if (method.Equals("GET"))
                        {
                            return new TestWebResponse(XmlHelper.Serialize(storageService));
                        }
                    }
                }
            }
            else
            {
                if (method.Equals("DELETE"))
                {
                    this.StorageAccounts.Remove(storageService);
                    return new TestWebResponse("Storage service has been deleted.");
                }
            }

            return new TestWebResponse("Invalid request.");
        }

        private bool ValidateStorageService(CreateStorageServiceInput createStorageService)
        {
            if (createStorageService == null)
            {
                return false;
            }

            return !string.IsNullOrEmpty(createStorageService.ServiceName)
                   && !this.StorageAccounts.Any(x => x.ServiceName.Equals(createStorageService.ServiceName))
                   && ((string.IsNullOrEmpty(createStorageService.AffinityGroup)
                        && !string.IsNullOrEmpty(createStorageService.Location))
                       || (string.IsNullOrEmpty(createStorageService.Location)
                           && !string.IsNullOrEmpty(createStorageService.AffinityGroup)))
                   && !string.IsNullOrEmpty(createStorageService.Label);
        }

        private TestWebResponse ProcessHostedServicesRequest(
            string method, List<string> segments, string body, string query)
        {
            if (!segments.Any())
            {
                if (method.Equals("GET"))
                {
                    return this.ListHostedServices();
                }

                if (method.Equals("POST"))
                {
                    var createHostedService = XmlHelper.Deserialize<CreateHostedService>(body);
                    if (this.ValidateHostedService(createHostedService))
                    {
                        this.HostedServices.Add(
                            new HostedServiceTestMock
                                {
                                    HostedService =
                                        new HostedService
                                            {
                                                ServiceName =
                                                    createHostedService.ServiceName, 
                                                HostedServiceProperties =
                                                    new HostedServiceProperties
                                                        {
                                                            Label
                                                                =
                                                                createHostedService
                                                                .Label, 
                                                            Description
                                                                =
                                                                createHostedService
                                                                .Description, 
                                                            Location
                                                                =
                                                                createHostedService
                                                                .Location, 
                                                            AffinityGroup
                                                                =
                                                                createHostedService
                                                                .AffinityGroup, 
                                                            Status
                                                                =
                                                                HostedServiceStatuses
                                                                .Created, 
                                                            DateCreated
                                                                =
                                                                DateTime
                                                                .Now, 
                                                            DateLastModified
                                                                =
                                                                DateTime
                                                                .Now
                                                        }
                                            }
                                });

                        return new TestWebResponse(string.Empty);
                    }

                    return new TestWebResponse("Invalid request.");
                }
            }

            var serviceName = segments.First();
            if (!this.HostedServices.Select(x => x.HostedService).Any(x => x.ServiceName.Equals(serviceName)))
            {
                return new TestWebResponse(string.Format("Hosted service {0} not found.", serviceName));
            }

            segments.RemoveAt(0);
            if (!segments.Any())
            {
                if (method.Equals("GET"))
                {
                    return this.GetHostedServiceProperties(serviceName, query.Contains("embed-detail=true"));
                }

                if (method.Equals("DELETE"))
                {
                    var hostedService =
                        this.HostedServices.Select(x => x.HostedService).First(x => x.ServiceName.Equals(serviceName));
                    this.HostedServices.Remove(this.HostedServices.First(x => x.HostedService == hostedService));

                    return
                        new TestWebResponse(
                            string.Format("Hosted service {0} has been removed", hostedService.ServiceName));
                }
            }

            if (segments.First().Equals("certificates"))
            {
                segments.RemoveAt(0);
                if (!segments.Any())
                {
                    if (method.Equals("GET"))
                    {
                        return this.ListHostedServiceCertificates(serviceName);
                    }
                }

                var certData = segments.First();
                segments.RemoveAt(0);

                if (!segments.Any())
                {
                    if (method.Equals("DELETE"))
                    {
                        var parts = certData.Split(new[] { "-" }, StringSplitOptions.RemoveEmptyEntries);
                        if (parts.Length == 2)
                        {
                            var hostedServiceMock =
                                this.HostedServices.First(x => x.HostedService.ServiceName.Equals(serviceName));
                            var certificate =
                                hostedServiceMock.Certificates.FirstOrDefault(
                                    x => x.Thumbprint.Equals(parts[1]) && x.ThumbprintAlgorithm.Equals(parts[0]));
                            if (certificate != null)
                            {
                                hostedServiceMock.Certificates.Remove(certificate);
                            }
                        }
                    }
                }

                return new TestWebResponse("Invalid request.");
            }

            if (segments.First().Equals("deployments"))
            {
                return this.ProcessDeploymentRequest(method, segments, serviceName);
            }

            if (segments.First().Equals("deploymentslots"))
            {
                segments.RemoveAt(0);
                if (!segments.Any())
                {
                    return new TestWebResponse("Invalid request.");
                }

                var deploymentSlot = segments.First();
                segments.RemoveAt(0);

                if (!segments.Any()
                    && (deploymentSlot.Equals(DeploymentSlots.Production.ToString().ToLower())
                        || deploymentSlot.Equals(DeploymentSlots.Staging.ToString().ToLower())))
                {
                    var hostedService =
                        this.HostedServices.Select(x => x.HostedService).First(x => x.ServiceName.Equals(serviceName));
                    var deploymentSlotEnumValue = DeploymentSlots.Production;
                    if (deploymentSlot.Equals(DeploymentSlots.Staging.ToString().ToLower()))
                    {
                        deploymentSlotEnumValue = DeploymentSlots.Staging;
                    }

                    if (method.Equals("GET"))
                    {
                        var deployment =
                            hostedService.Deployments.FirstOrDefault(
                                x => x.DeploymentSlot.Equals(deploymentSlotEnumValue));
                        if (deployment != null)
                        {
                            return new TestWebResponse(XmlHelper.Serialize(deployment));
                        }
                    }

                    if (method.Equals("DELETE"))
                    {
                        var deployment =
                            hostedService.Deployments.FirstOrDefault(
                                x => x.DeploymentSlot.Equals(deploymentSlotEnumValue));
                        if (deployment == null)
                        {
                            return new TestWebResponse(string.Format("Can't find deployment"));
                        }

                        hostedService.Deployments.Remove(deployment);
                        return new TestWebResponse("Deployment has been deleted");
                    }

                    if (method.Equals("POST"))
                    {
                        var createDeployment = XmlHelper.Deserialize<CreateDeployment>(body);
                        if (this.ValidateDeployment(hostedService, createDeployment, deploymentSlotEnumValue))
                        {
                            hostedService.Deployments.Add(
                                new Deployment
                                    {
                                        Name = createDeployment.Name, 
                                        DeploymentSlot = deploymentSlotEnumValue, 
                                        PrivateID = Guid.NewGuid().ToString(), 
                                        Status = DeploymentStatuses.Running, 
                                        Label = createDeployment.Label, 
                                        Url = string.Empty, 
                                        Configuration = createDeployment.Configuration, 
                                        UpgradeDomainCount = 1, 
                                        SdkVersion = "1.8.31004.1351", 
                                        Locked = false, 
                                        RollbackAllowed = false, 
                                        CreatedTime = DateTime.Now, 
                                        LastModifiedTime = DateTime.Now
                                    });

                            return new TestWebResponse("Deployment created.");
                        }
                    }
                }
            }

            return new TestWebResponse("Invalid request.");
        }

        private TestWebResponse ListHostedServiceCertificates(string serviceName)
        {
            var certificates = new Certificates
                                   {
                                       Items =
                                           this.HostedServices.First(
                                               x => x.HostedService.ServiceName.Equals(serviceName))
                                               .Certificates.ToList()
                                   };

            return new TestWebResponse(XmlHelper.Serialize(certificates));
        }

        private TestWebResponse ProcessDeploymentRequest(string method, List<string> segments, string serviceName)
        {
            segments.RemoveAt(0);
            if (!segments.Any())
            {
                return new TestWebResponse("Invalid request.");
            }

            var deploymentName = segments.First();
            segments.RemoveAt(0);

            if (segments.Any())
            {
                return new TestWebResponse("Invalid request.");
            }

            var hostedService =
                this.HostedServices.Select(x => x.HostedService).First(x => x.ServiceName.Equals(serviceName));
            var deployment = hostedService.Deployments.FirstOrDefault(x => x.Name.Equals(deploymentName));
            if (deployment == null)
            {
                return new TestWebResponse(string.Format("Can't find deployment with name {0}", deploymentName));
            }

            if (method.Equals("GET"))
            {
                return new TestWebResponse(XmlHelper.Serialize(deployment));
            }

            if (method.Equals("DELETE"))
            {
                hostedService.Deployments.Remove(deployment);
                return new TestWebResponse(string.Format("Deployment {0} has been deleted", deploymentName));
            }

            return new TestWebResponse("Invalid request.");
        }

        private bool ValidateHostedService(CreateHostedService createHostedService)
        {
            if (createHostedService == null)
            {
                return false;
            }

            return !string.IsNullOrEmpty(createHostedService.ServiceName)
                   && !string.IsNullOrEmpty(createHostedService.Label)
                   && ((!string.IsNullOrEmpty(createHostedService.Location)
                        && string.IsNullOrEmpty(createHostedService.AffinityGroup))
                       || (!string.IsNullOrEmpty(createHostedService.AffinityGroup)
                           && string.IsNullOrEmpty(createHostedService.Location)));
        }

        private bool ValidateDeployment(
            HostedService hostedService, CreateDeployment createDeployment, DeploymentSlots deploymentSlot)
        {
            if (createDeployment == null)
            {
                return false;
            }

            return !string.IsNullOrEmpty(createDeployment.Name)
                   && !hostedService.Deployments.Any(x => x.Name.Equals(createDeployment.Name))
                   && !hostedService.Deployments.Any(x => x.DeploymentSlot.Equals(deploymentSlot))
                   && !string.IsNullOrEmpty(createDeployment.PackageUrl)
                   && !string.IsNullOrEmpty(createDeployment.Configuration)
                   && !string.IsNullOrEmpty(createDeployment.Label);
        }

        private TestWebResponse GetHostedServiceProperties(string serviceName, bool embedDetails)
        {
            var hostedService =
                this.HostedServices.Select(x => x.HostedService).First(x => x.ServiceName.Equals(serviceName));
            var deployments = hostedService.Deployments;
            if (!embedDetails)
            {
                hostedService.Deployments = null;
            }

            var response = new TestWebResponse(XmlHelper.Serialize(hostedService));
            hostedService.Deployments = deployments;

            return response;
        }

        private TestWebResponse ListHostedServices()
        {
            var hostedServices = new HostedServices
                                     {
                                         Items = this.HostedServices.Select(x => x.HostedService).ToList()
                                     };

            return new TestWebResponse(XmlHelper.Serialize(hostedServices));
        }

        private TestWebResponse ListStorageServices()
        {
            var storageServices = new StorageServices { Items = this.StorageAccounts.ToList() };

            return new TestWebResponse(XmlHelper.Serialize(storageServices));
        }
    }
}